//
// Created by THDMI on 24-3-30.
// You must known, this file use `cesanta/mongoose` software, which using protocol GPLv2, so open source you do it.
//

#ifndef LDSS_NETWORK_HTTP_H
#define LDSS_NETWORK_HTTP_H

#include "mongoose.h"

#include <atomic>
#include <condition_variable>
#include <functional>
#include <string>
#include <unordered_map>
#include <vector>

/// @brief LDSS MQ Component
namespace ldss_mq {
    /// @brief structure of request message
    struct StructReqMessage {
    };

    /// @brief structure of response message
    struct StructRepMessage {
        std::uint16_t code; ///< response status code
    };

    /// @brief structure of response option for connection, such as 'HTTP status code'
    struct StructRepOption {
    };

    /// @brief enum of HTTP method
    enum class EmMethodHTTP : uint8_t {
        METHOD_UNDEFINED = 0x00, ///< undefined method
        METHOD_GET, ///< GET method
        METHOD_POST, ///< POST method
        METHOD_PUT, ///< PUT method
        METHOD_DELETE ///< DELETE method
    };

    /// @brief function value of request callback for server
    using FuncReqCallback = std::function<void(const StructReqMessage &req_, StructRepMessage &rep_,
                                               StructRepOption &body_)>;

    /// @brief function value of response callback for client
    using FuncRepCallback = std::function<void(const StructRepMessage &rep_, const std::vector<char> &body_)>;

    class LDSS_NetHTTP {
    public:
        LDSS_NetHTTP() = default;

        ~LDSS_NetHTTP();

        /// @brief
        /// @author
        /// @date
        /// @param name_ [in]
        /// @param option_ [in]
        /// @param err_ [in]
        /// @return
        bool Init(const std::string &name_, const std::string &option_, std::string &err_);

        /// @brief
        /// @author
        /// @date
        /// @param router_ [in]
        /// @param callback_ [in]
        /// @param err_ [out]
        /// @return
        bool ServerRegister(const std::string &router_, const FuncReqCallback &callback_, std::string &err_) {
            return this->ServerRegister(EmMethodHTTP::METHOD_UNDEFINED, router_, callback_, err_);
        }

        /// @brief
        /// @author
        /// @date
        /// @param method_ [in]
        /// @param router_ [in]
        /// @param callback_ [in]
        /// @param err_ [out]
        /// @return
        bool ServerRegister(EmMethodHTTP method_, const std::string &router_,
                            const FuncReqCallback &callback_, std::string &err_);

        /// @brief
        /// @author
        /// @date
        /// @param addr_ [in]
        /// @param port_ [in]
        /// @param err_ [out]
        /// @return
        bool ServerStart(const std::string &addr_, std::uint16_t port_, std::string &err_);

        /// @brief
        /// @author
        /// @date
        void ServerStop();

        /// @brief
        /// @author
        /// @date
        /// @param addr_ [in]
        /// @param port_ [in]
        /// @param err_ [out]
        /// @return
        bool OptionDefaultTargetSet(const std::string &addr_, std::uint16_t port_, std::string &err_) {
            this->p_default_dst_addr = addr_;
            this->p_default_dst_port = port_;
            return addr_.empty();
        }

        /// @brief
        /// @author
        /// @date
        /// @param addr_ [out]
        /// @param port_ [out]
        void OptionDefaultTargetGet(std::string &addr_, std::uint16_t &port_) const {
            addr_ = this->p_default_dst_addr;
            port_ = this->p_default_dst_port;
        }

        /// @brief
        /// @author
        /// @date
        /// @param method_ [in]
        /// @param router_ [in]
        /// @param data_ [in]
        /// @param func_ [in]
        /// @return
        bool SendDefault(EmMethodHTTP method_, const std::string_view &router_,
                         const std::vector<char> &data_, const FuncRepCallback &func_) {
            return this->Send(this->p_default_dst_addr, this->p_default_dst_port, method_, router_, data_, func_);
        }

        /// @brief
        /// @author
        /// @date
        /// @param addr_ [in]
        /// @param port_ [in]
        /// @param method_ [in]
        /// @param router_ [in]
        /// @param data_ [in]
        /// @param func_ [out]
        /// @return
        bool Send(const std::string_view &addr_, std::uint16_t port_,
                  EmMethodHTTP method_, const std::string_view &router_,
                  const std::vector<char> &data_, const FuncRepCallback &func_);

    private:
        bool p_flag_init{}; ///< flag of initialize the class
        std::string p_name{}; ///< name of the user to log output
        std::string p_default_dst_addr{"localhost"}; ///< default address IPv4 message to send
        std::uint16_t p_default_dst_port{8080}; ///< default port target listen message to send
        std::string p_server_addr{"localhost"}; ///< default address IPv4 message to send
        std::uint16_t p_server_port{8080}; ///< default port target listen message to send
        std::unordered_map<std::string, std::unordered_map<std::string, FuncReqCallback> > p_uom_callback_server{
            {"UNDEFINED", {}},
            {"GET", {}},
            {"POST", {}},
            {"PUT", {}},
            {"DELETE", {}}
        }; ///< callback function server response
        std::atomic_bool p_flag_running_server{}; ///< whether server already running
        mg_mgr p_server{}; ///< object of mongoose basic structure for server
        mg_mgr p_client{}; ///< object of mongoose basic structure for client
        std::mutex p_mtx_server{}; ///< unique mutex for server exit condition variable
        std::condition_variable p_cov_server{}; ///< condition variable for server exit
    };
}

#endif //LDSS_NETWORK_HTTP_H
